﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Pibby.Core.IO;
using invrt;
using Pibby.Core.Models;
using Pibby.Core.Persistence;
using Pibby.Core.Views;
using Pibby.Core.Web;
using System.IO;
using Pibby.Core.Feeds;
using Pibby.Core.ViewModels;
//using Id3Tag;
//using Id3Tag.HighLevel;
using UsbEject.Library;
using Pibby.Core.Playlist;
using dogd;

namespace Pibby.Core.Presenters
{
	public class PodcatcherPresenter : Presenter<IPodcatcherView, IPodcatcherModel>
	{
		#region Attributes

		private BackgroundProcessing _fileDownloadWorker;
		private ParallelDownloader _downloader = new ParallelDownloader(5);
		private BackgroundProcessing _rssFeedWorker;
		private BackgroundProcessing _validationWorker;
		private BackgroundProcessing _transferWorker;
		private BackgroundProcessing _cleanupWorker;

		private bool _swallowConfigChanges;

		#endregion Attributes

		#region Constructor

		public PodcatcherPresenter(IPodcatcherView view, IPodcatcherModel model)
			: base(view, model)
		{
		}

		#endregion Constructor

		#region Event Wiring

		protected override void SubscribeToClientEvents()
		{
			this.Model.SettingsChanged += new EventHandler(Model_SettingsChanged);
			this.Model.PortableMediaArrived += new EventHandler<RemovableDriveEventArgs>(Model_PortableMediaArrived);
			this.Model.PortableMediaRemoved += new EventHandler<RemovableDriveEventArgs>(Model_PortableMediaRemoved);

			this.View.Initialising += new EventHandler(View_Initialising);
			this.View.InitialiseComplete += new EventHandler(View_InitialiseComplete);
			this.View.ClosingView += new EventHandler(View_ClosingView);
			this.View.CheckForEpisodes += new EventHandler(View_CheckForEpisodes);
			this.View.DownloadEpisodes += new EventHandler<EpisodeDownloadEventArgs>(View_DownloadEpisodes);
			this.View.IgnoreEpisode += new EventHandler<EpisodeEventArgs>(View_IgnoreEpisode);
			this.View.PodcastSelected += new EventHandler<PodcastEventArgs>(View_PodcastSelected);
			this.View.SearchEpisodes += new EventHandler<SearchEventArgs>(View_SearchEpisodes);
			this.View.SearchPodcast += new EventHandler<PodcastSearchEventArgs>(View_SearchPodcast);
			this.View.SearchPodcasts += new EventHandler<SearchEventArgs>(View_SearchPodcasts);
			this.View.AddPodcast += new EventHandler<NewPodcastEventArgs>(View_AddPodcast);
			this.View.RefreshNavigationList += new EventHandler(View_RefreshNavigationList);
			this.View.PortableMediaSettingsChanged += new EventHandler<PortableMediaSettingsEventArgs>(View_PortableMediaSettingsChanged);
			this.View.RemovableDeviceArrived += new EventHandler<RemovableDriveEventArgs>(View_RemovableDeviceArrived);
			this.View.RemovableDeviceRemoved += new EventHandler<RemovableDriveEventArgs>(View_RemovableDeviceRemoved);
			//this.View.SubViewChanged += new EventHandler<SubViewEventArgs>(View_SubViewChanged);
			this.View.ImportPodcasts += new EventHandler<FilePersistenceEventArgs>(View_ImportPodcasts);
			this.View.CopyFiles += new EventHandler<MultiFilePersistenceEventArgs>(View_CopyFiles);
			this.View.CopyPlaylist += new EventHandler<FilePersistenceEventArgs>(View_CopyPlaylist);
			this.View.TransferContent += new EventHandler<TransferCommandEventArgs>(View_TransferContent);

			this.View.RefreshPortableDevice += new EventHandler(View_RefreshPortableDevice);

			this.View.DownloadsSelected += new EventHandler(View_DownloadsSelected);
			this.View.NewEpisodesSelected += new EventHandler(View_NewEpisodesSelected);
			this.View.SubscriptionsSelected += new EventHandler(View_SubscriptionsSelected);
		}

		void View_SubscriptionsSelected(object sender, EventArgs e)
		{
			var selectedPodcasts = this.Model.LoadSubscriptions();

			// switch view ???
			this.View.ShowSubscriptionList(selectedPodcasts);
		}

		void View_NewEpisodesSelected(object sender, EventArgs e)
		{
			this.View.LoadNewEpisodes(this.Model.LoadEpisodes(EpisodeLifecycle.NotDownloaded));
		}

		void View_DownloadsSelected(object sender, EventArgs e)
		{
			this.View.LoadDownloadedEpisodes(this.Model.LoadEpisodes(EpisodeLifecycle.Downloaded));
		}

		public override void UnsubscribeToClientEvents()
		{
			this.View.Initialising -= View_Initialising;
			this.View.InitialiseComplete -= View_InitialiseComplete;
			this.View.ClosingView -= View_ClosingView;
			this.Model.SettingsChanged -= Model_SettingsChanged;

			this.View.CheckForEpisodes -= View_CheckForEpisodes;
			this.View.IgnoreEpisode -= View_IgnoreEpisode;
			this.View.DownloadEpisodes -= View_DownloadEpisodes;
			this.View.RefreshNavigationList -= View_RefreshNavigationList;

		}

		#endregion Event Wiring

		#region Model Events

		void Model_PortableMediaArrived(object sender, RemovableDriveEventArgs e)
		{
			IDrive drive = this.Model.PortableMedia.Drive;

			long total = 0;
			long available = 0;

			if (drive != null)
			{
				total = drive.TotalSize;
				available = drive.AvailableFreeSpace;

				// now look for playlists...
				var playlistLister = new FileListVisitor();

				var factory = InversionOfController.Factory.CreateTypeOf<IFolderFactory>();
				var rootFolder = factory.GetFolder (drive.Name);

				playlistLister.ListContentsOf(rootFolder,
												System.IO.SearchOption.AllDirectories,
												FileConstants.M3uFileFilter);

				if (playlistLister.Items.Count > 0)
				{
					// add these to the list...
				}
			}

			var vm = new PortableMediaViewModel(this.Model.PortableMedia.Enabled,
														this.Model.PortableMedia.IsConnected(),
														this.Model.PortableMedia.DriveName,
														this.Model.PortableMedia.VolumeName,
														total,
														available,
														0);
			this.View.RefreshDeviceStatus(vm);
		}

		void Model_PortableMediaRemoved(object sender, RemovableDriveEventArgs e)
		{
			var vm = new PortableMediaViewModel(this.Model.PortableMedia.Enabled,
														this.Model.PortableMedia.IsConnected(),
														e.DriveName,
														e.VolumeName,
														0,
														0,
														0);

			this.View.RefreshDeviceStatus(vm);
		}

		#endregion Model Events

		#region View Event Handlers

		void View_RefreshPortableDevice(object sender, EventArgs e)
		{
			if (this.Model.PortableMedia.Enabled && this.Model.PortableMedia.IsConnected())
			{
				IDrive drive = this.Model.PortableMedia.Drive;

				long total = 0;
				long available = 0;

				if (drive != null)
				{
					total = drive.TotalSize;
					available = drive.AvailableFreeSpace;

					// now look for playlists...
					var playlistLister = new FileListVisitor();

					var factory = InversionOfController.Factory.CreateTypeOf<IFolderFactory>();
					var rootFolder = factory.GetFolder(drive.Name);

					playlistLister.ListContentsOf(rootFolder,
													System.IO.SearchOption.AllDirectories,
													FileConstants.M3uFileFilter);

					if (playlistLister.Items.Count > 0)
					{
						// add these to the list...
					}
				}

				var vm = new PortableMediaViewModel(this.Model.PortableMedia.Enabled,
															this.Model.PortableMedia.IsConnected(),
															this.Model.PortableMedia.DriveName,
															this.Model.PortableMedia.VolumeName,
															total,
															available,
															0);

				this.View.RefreshDeviceStatus(vm);
			}
		}

		void View_CopyPlaylist(object sender, FilePersistenceEventArgs e)
		{
			MediaPlaylist playlist = null;

			var factory = InversionOfController.Factory.CreateTypeOf<ITextReaderFactory>();

			using (var reader = factory.FromFile(e.Path))
			{
				if (e.Path.EndsWith(PlaylistConstants.Mp3PlaylistExtension))
				{
					playlist = M3uPlaylistReader.LoadFrom(reader);
				}
				else if (e.Path.EndsWith(PlaylistConstants.WindowsPlaylistExtension))
				{
					playlist = WindowsPlaylistReader.LoadFrom(reader);
				}
			}

			if (playlist != null)
			{
				// 
				if (this.Model.PortableMedia.IsConnected() && this._transferWorker == null)
				{
					var collection = new TransferableCollection();

					var canonical = CanonicalPlaylistBuilder.Build(playlist, System.IO.Path.GetDirectoryName(e.Path));

					foreach (var item in canonical)
					{
						collection.Add(new Transferable()
						{
							Id = item.Id,
							TargetFolder = this.Model.PortableMedia.Drive.Name, // root folder...
							Source = item.Path
						});
					}

					// check there is space ???

					var args = new TransferFilesArgs(collection, AfterCopyBehaviour.KeepContent);

					if (args.Files.Count > 0)
					{
						this.View.TransferStarting(args.Files.Count);

						SpinUpCopyThread(args);
					}
				}

			}
		}

		void View_CopyFiles(object sender, MultiFilePersistenceEventArgs e)
		{
			if (this.Model.PortableMedia.IsConnected() && this._transferWorker == null)
			{
				var collection = new TransferableCollection();

				foreach (var file in e.Paths)
				{
					collection.Add(new Transferable()
					{
						Source = file,
						TargetFolder = this.Model.PortableMedia.Drive.Name, // root folder...
					});
				}

				// check there is space ???

				var args = new TransferFilesArgs(collection, AfterCopyBehaviour.KeepContent);

				if (args.Files.Count > 0)
				{
					this.View.TransferStarting(args.Files.Count);
					SpinUpCopyThread(args);
				}
			}

		}

		void View_ImportPodcasts(object sender, FilePersistenceEventArgs e)
		{
			var factory = InversionOfController.Factory.CreateTypeOf<ITextReaderFactory>();

			using (var reader = factory.FromFile(e.Path))
			{
				this.Model.ImportSubscriptions(reader);
				this.View.LoadSubscriptions(this.Model.LoadSubscriptions());
			}
		}



		void View_PortableMediaSettingsChanged(object sender, PortableMediaSettingsEventArgs e)
		{
			this.Model.CheckPortableMediaStatus();

			long total = 0;
			long available = 0;

			if (this.Model.PortableMedia.IsConnected())
			{
				IDrive drive = this.Model.PortableMedia.Drive;

				if (drive != null)
				{
					total = drive.TotalSize;
					available = drive.AvailableFreeSpace;
				}
			}
		}

		//void View_SubViewChanged(object sender, SubViewEventArgs e)
		//{
		//    // depending on what else is going on - we may want to 
		//    // block changes here...
		//}

		void View_RemovableDeviceArrived(object sender, RemovableDriveEventArgs e)
		{
			this.Model.CheckPortableMediaArrived(e);
		}

		void View_RemovableDeviceRemoved(object sender, RemovableDriveEventArgs e)
		{
			this.Model.CheckPortableMediaRemoved(e);
		}

		void View_TransferContent(object sender, TransferCommandEventArgs e)
		{
			if (this.Model.PortableMedia.IsConnected())
			{
				var args = new TransferFilesArgs(this.Model.TransferrableContent,
				                                            e.DeleteFiles ?
				                                                AfterCopyBehaviour.DeleteContent
				                                                : AfterCopyBehaviour.KeepContent);

                if (args.Files.Count > 0)
                {
                    this.View.TransferStarting(args.Files.Count);
                    this.SpinUpTransferFilesThread(args);
                }
                else
                {
                    this.View.TransferFinished(args.Files.Count);
                }
			}
		}

		void View_EjectDevice(object sender, EventArgs e)
		{
			if (this.Model.PortableMedia.CanBeEjected())
			{
				// check we're not currently transferring to drive...
				this.Model.PortableMedia.Eject();
			}
		}

		void View_AddPodcast(object sender, NewPodcastEventArgs e)
		{
			var args = new DownloadFilesArgs(new Downloadable()
												{
													Uri = e.Uri,
													InMemoryDownload = true,
													FriendlyName = string.Format("Getting feed from \'{0}\' ", e.Uri.AbsoluteUri),
													Tag = new SubscriptionCookie
													{
														Url = e.Uri.AbsoluteUri,
														SuggestedName = e.Name
													}
												});

			SpinUpValidateFeedThread(args);
		}

		void View_SearchPodcasts(object sender, SearchEventArgs e)
		{
			var result = this.Model.SearchPodcasts(e.Search);

			this.View.LoadSearchResults(result);
		}

		void View_SearchPodcast(object sender, PodcastSearchEventArgs e)
		{
			//var result = this.Model.SearchEpisodes(e.Guid, e.Search);

			//this.View.LoadEpisodes(result);
		}

		void View_SearchEpisodes(object sender, SearchEventArgs e)
		{
			//var result = this.Model.SearchEpisodes(e.Search);

			//this.View.LoadEpisodes(result);
		}

		void View_PodcastSelected(object sender, PodcastEventArgs e)
		{
			Contract.EnsureThat(e.DbKey > 0);

			var podcast = this.Model.LoadSubscription(e.DbKey);

			EpisodeCollection selectedEpisodes = this.Model.LoadEpisodes(podcast);

			this.View.LoadPodcastEpisodes(selectedEpisodes);
		}

		void View_RefreshNavigationList(object sender, EventArgs e)
		{
			this.View.LoadSubscriptions(this.Model.LoadSubscriptions());
		}

		void View_Initialising(object sender, EventArgs e)
		{
		}

		void View_InitialiseComplete(object sender, EventArgs e)
		{
			this.Model.RebaseReputations();

			this.View.SettingsLoaded(this.Model.Settings);
			this.View.LoadSubscriptions(this.Model.LoadSubscriptions());
			this.Model.CheckPortableMediaStatus();

			this.View.LoadNewEpisodes(this.Model.LoadEpisodes());
		}
		
		void View_ClosingView(object sender, EventArgs e)
		{
			this._swallowConfigChanges = true;

			this.Model.Settings.MainWindow.Top = this.View.Top;
			this.Model.Settings.MainWindow.Left = this.View.Left;
			this.Model.Settings.MainWindow.Width = this.View.Width;
			this.Model.Settings.MainWindow.Height = this.View.Height;
			this.Model.Settings.MainWindow.SplitterDistance = this.View.SplitterDistance;
			this.Model.Settings.MainWindow.CurrentView = this.View.CurrentView;

            this.Model.Settings.Save();
		}

		#endregion View Event Handlers

		#region Model Event Handlers

		void Model_SettingsChanged(object sender, EventArgs e)
		{
			if (!this._swallowConfigChanges)
			{
				this.View.SettingsChanged(this.Model.Settings);
			}
		}

		#endregion Model Event Handlers


		/// <summary>
		/// Handles the CheckForEpisodes event of the View control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		void View_CheckForEpisodes(object sender, EventArgs e)
		{
			var args = new DownloadFilesArgs(this.Model.DownloadableFeeds);

			this.View.RssRefreshStarting(args.Downloads.Count);

			SpinUpRefreshFeedThread(args);
		}

		void View_IgnoreEpisode(object sender, EpisodeEventArgs e)
		{
			// not very efficient...
			this.Model.UpdateEpisodes(e.DbKeys, EpisodeLifecycle.Ignore);
			this.View.LoadNewEpisodes(this.Model.LoadEpisodes());
		}

		private void View_DownloadEpisodes(object sender, EpisodeDownloadEventArgs e)
		{
			var files = this.Model.LoadEpisodesForDownload(e.DbKeys);

			var factory = InversionOfController.Factory.CreateTypeOf<IFolderFactory>();
			
			foreach (var file in files)
			{
				var folder = factory.GetFolder(file.TargetFolder);

				if (!folder.Exists)
				{
					folder.Create();
				}
			}

			var args = new DownloadFilesArgs(files);

            if (args.Downloads.Count > 0)
            {
                this.View.EpisodeDownloadStarting(args.Downloads.Count);

				//foreach (var download in args.Downloads)
				//{
				//    this._downloader.EnqueueFileToDownload(download);
				//}

                this.SpinUpDownloadThread(args);
            }
            else
            {
                this.View.EpisodeDownloadFinished(args.Downloads.Count);
            }
		}


		#region Background Thread Event Handlers

		#region File Download Thread

		private void SpinUpDownloadThread(DownloadFilesArgs args)
		{
			if (this._fileDownloadWorker == null)
			{
                this.DownloadCount = 0;

				this._fileDownloadWorker = BackgroundProcessing.Create<DownloadFilesArgs, ProgressEventArgs>(BackgroundMethods.DownloadFiles,
												FileDownloadBackgroundThread_ProgressChanged,
												FileDownloadBackgroundThread_RunWorkerCompleted,
												FileDownloadBackgroundThread_OnException,
												args);

				this._fileDownloadWorker.Run();
			}
		}

		private void FileDownloadBackgroundThread_ProgressChanged(ProgressEventArgs args)
		{
			try
			{
				if (args != null && args.Tag != null)
				{
					var tag = args.Tag as IFileDownload;

					if (tag != null)
					{
						this.View.EpisodeDownloadProgress((int)args.CurrentProgress);

						if (args.Status == ProgressEventStatus.Complete)
						{
							Episode episode = this.Model.LoadEpisode(tag.Id);

							if (episode != null)
							{
                                ++this.DownloadCount;
								episode.Stage = EpisodeLifecycle.Downloaded;
								this.Model.UpdateEpisode(episode);

								var podcast = this.Model.LoadSubscription(episode.SubscriptionID); // .LoadSubscriptions().Where(p => p.SafeName.Equals(episode.PodcastName)).FirstOrDefault();

								if (podcast != null)
								{
									podcast.Whuffie.Esteem(WhuffieRules.EsteemForDownload);
									this.Model.UpdatePodcast(podcast);
								}

								bool tagOnDownload = true;

								if (tagOnDownload)
								{
									TagLib.File f = TagLib.File.Create(tag.SavedTo);
									f.Tag.Album = episode.PodcastName;
									f.Tag.Comment = episode.Description;
									f.Tag.Title = episode.Name;
									f.Save();
								}
							}

						}
					}
				}
			}
			catch (PersistenceException ex)
			{
				Logging.Log.Error(ex.Message);
			}
		}

        private int DownloadCount { get; set; }

		void FileDownloadBackgroundThread_RunWorkerCompleted()
		{
			try
			{
                this.View.EpisodeDownloadFinished(this.DownloadCount);
				this.View.LoadNewEpisodes(this.Model.LoadEpisodes());

				this._fileDownloadWorker.Dispose();
				this._fileDownloadWorker = null;
			}
			catch (PersistenceException ex)
			{
				Logging.Log.Error(ex.Message);
			}
		}

		private void FileDownloadBackgroundThread_OnException(Exception ex)
		{
			try
			{
				this._fileDownloadWorker.Dispose();
				this._fileDownloadWorker = null;

                this.View.EpisodeDownloadFinished(this.DownloadCount);

				Logging.Log.Error("Exception downloading file");
				Logging.Log.Error(ex.ToString());
			}
			catch (PersistenceException persistence)
			{
				Logging.Log.Error(persistence.Message);
			}
		}

		#endregion File Download Thread

		#region Rss Feed Thread

		private void SpinUpRefreshFeedThread(DownloadFilesArgs args)
		{
			if (this._rssFeedWorker == null)
			{
                this.NewEpisodeCount = 0;

				this._rssFeedWorker = BackgroundProcessing.Create<DownloadFilesArgs, ProgressEventArgs>(BackgroundMethods.DownloadRssFeeds,
								RssFeedBackgroundThread_ProgressChanged,
								RssFeedBackgroundThread_RunWorkerCompleted,
								RssFeedBackgroundThread_OnException,
								args);

				this._rssFeedWorker.Run();
			}
		}

		private void RssFeedBackgroundThread_ProgressChanged(ProgressEventArgs args)
		{
			try
			{
				if (args != null && args.Tag != null)
				{
					var tag = args.Tag as PodcastRefreshCookie;

					if (tag != null && tag.Content != null)
					{
						this.View.RssRefreshProgress((int)args.CurrentProgress);

						try
						{
							this.Model.UpdateFeed(tag.Id, tag.Content);
						}
						catch (Feeds.FeedReadException ex)
						{
							Logging.Log.Error(ex.Message);
							// report the error...
							//this.View.ReportError(tag.Id + " " + ex.Message);
						}
					}
				}
			}
			catch (PersistenceException ex)
			{
				Logging.Log.Error(ex.Message);
			}
		}

        private int NewEpisodeCount { get; set; }

		private void RssFeedBackgroundThread_RunWorkerCompleted()
		{
			try
			{
				this._rssFeedWorker.Dispose();
				this._rssFeedWorker = null;

				var podcasts = this.Model.LoadSubscriptions();
				podcasts.Sort();

				int autoDownloadThreshold = 1;

				if (podcasts.Count > 1)
				{
					int highestScore = podcasts[0].Score;
					autoDownloadThreshold = (25 * highestScore) / 100;
				}

				var episodes = this.Model.LoadEpisodes();

				//var toDownload = new List<string>();

				//foreach (var ep in episodes)
				//{
				//    // find podcast and look at whuffie...
				//    // find age of podcast - if whuffie >= 10
				//    // and age is this week - download it automatically....
				//    var podcast = podcasts.FindByName(ep.PodcastName);

				//    if (podcast != null 
				//        && (ep.Age == EpisodeAge.BrandNew || ep.Age == EpisodeAge.Newish))
				//    {
				//        ++this.NewEpisodeCount;

				//        // download everything for now...
				//        //if (podcast.Whuffie.Score > autoDownloadThreshold)
				//        {
				//            toDownload.Add(ep.Id);
				//        }
				//    }
				//}

                this.View.RssRefreshFinished(this.NewEpisodeCount);

				this.View.LoadNewEpisodes(episodes);

				//if (toDownload.Count > 0)
				//{
				//    var files = this.Model.LoadEpisodesForDownload(toDownload);

				//    var factory = InversionOfController.Factory.CreateTypeOf<IFolderFactory>();

				//    foreach (var file in files)
				//    {
				//        var folder = factory.GetFolder(file.TargetFolder);

				//        if (!folder.Exists)
				//        {
				//            folder.Create();
				//        }
				//    }

				//    var args = new DownloadFilesArgs(files);
 
				//    this.View.EpisodeDownloadStarting(args.Downloads.Count);
				//    this.SpinUpDownloadThread(args);

				//}
			}
			catch (PersistenceException ex)
			{
				Logging.Log.Error(ex.Message);
			}
		}

		private void RssFeedBackgroundThread_OnException(Exception ex)
		{
			this._rssFeedWorker.Dispose();
			this._rssFeedWorker = null;

            this.View.RssRefreshFinished(this.NewEpisodeCount);

			Logging.Log.Error("Exception downloading rss feed");
			Logging.Log.Error(ex.ToString());
		}

		#endregion Rss Feed Thread

		#region Validate Rss Feed Thread

		private void SpinUpValidateFeedThread(DownloadFilesArgs args)
		{
			if (this._validationWorker == null)
			{
				this._validationWorker = BackgroundProcessing.Create<DownloadFilesArgs, ProgressEventArgs>(
													BackgroundMethods.DownloadRssFeeds,
													ValidationBackgroundThread_ProgressChanged,
													ValidationBackgroundThread_RunWorkerCompleted,
													ValidationBackgroundThread_OnException,
													args);

				this._validationWorker.Run();
			}
		}

		private void ValidationBackgroundThread_ProgressChanged(ProgressEventArgs args)
		{
			if (args != null && args.Tag != null)
			{
				var tag = args.Tag as SubscriptionCookie;

				if (tag != null)
				{
					try
					{
						this.Model.CreateFeed(new Uri (tag.Url), tag.SuggestedName, tag.Content);
					}
					catch (Feeds.FeedReadException ex)
					{ 
						// report the error...
						this.View.ReportError(tag.Url + " " + ex.Message);
					}
				}
			}
		}

		void ValidationBackgroundThread_RunWorkerCompleted()
		{
			this._validationWorker.Dispose();
			this._validationWorker = null;

			this.View.LoadSubscriptions(this.Model.LoadSubscriptions());
		}

		private void ValidationBackgroundThread_OnException(Exception ex)
		{
			this._validationWorker.Dispose();
			this._validationWorker = null;

			Logging.Log.Error("Exception validating rss feed");
			Logging.Log.Error(ex.ToString());
		}

		#endregion Validate Rss Feed Thread

		#region File Transfer Thread

		private void SpinUpTransferFilesThread(TransferFilesArgs args)
		{
			if (this._transferWorker == null)
			{
                this.TransferCount = 0;

				this._transferWorker = BackgroundProcessing.Create<TransferFilesArgs, ProgressEventArgs>(BackgroundMethods.TransferFiles,
					TransferBackgroundThread_ProgressChanged,
					TransferBackgroundThread_RunWorkerCompleted,
					TransferBackgroundThread_OnException,
					args);

				this._transferWorker.Run();
			}
		}

        private int TransferCount { get; set; }

		private void TransferBackgroundThread_ProgressChanged(ProgressEventArgs args)
		{
			try
			{
				if (args != null && args.Tag != null)
				{
					var cookie = args.Tag as FileCopyCookie;

					this.View.TransferProgress((int)args.CurrentProgress);

					if (cookie != null)
					{
						if (string.IsNullOrEmpty(cookie.ExceptionMessage))
						{
                            ++this.TransferCount;
							this.Model.UpdateEpisodes(new List<int>() { cookie.Id },
														EpisodeLifecycle.Transferred);
						}
						else
						{
							Logging.Log.Error(cookie.ExceptionMessage);
						}
					}
				}
			}
			catch (PersistenceException ex)
			{
				Logging.Log.Error(ex.Message);
			}
		}
				
		void TransferBackgroundThread_RunWorkerCompleted()
		{
			this._transferWorker.Dispose();
			this._transferWorker = null;

			this.View.TransferFinished(this.TransferCount);

			this.SpinUpCleanupThread(new CleanupFolderArgs(this.Model.Settings.DownloadFolder));
				
			// re-enable controls...
			if (this.Model.PortableMedia.EjectOption == EjectOption.Auto && this.Model.PortableMedia.CanBeEjected())
			{
				this.Model.PortableMedia.Eject();
			}
			else
			{
				long total = 0;
				long available = 0;

				if (this.Model.PortableMedia.IsConnected())
				{
					IDrive drive = this.Model.PortableMedia.Drive;

					if (drive != null)
					{
						total = drive.TotalSize;
						available = drive.AvailableFreeSpace;
					}
				}

				//var vm = new PortableMediaViewModel(this.Model.PortableMedia.Enabled,
				//                                                        this.Model.PortableMedia.Connected,
				//                                                        this.Model.PortableMedia.DriveName,
				//                                                        this.Model.PortableMedia.VolumeName,
				//                                                        total,
				//                                                        available,
				//                                                        this.Model.PendingContent);

				//this.View.RefreshDeviceStatus(vm);
			}

			//this.View.TransferFinished();
		}

		private void TransferBackgroundThread_OnException(Exception ex)
		{
			this._transferWorker.Dispose();
			this._transferWorker = null;

			this.View.TransferFinished(this.TransferCount);

			if (ex.InnerException != null)
				this.View.ReportError(ex.InnerException.Message);
			else
				this.View.ReportError(ex.Message);
		}

		private void SpinUpCopyThread(TransferFilesArgs args)
		{
			if (this._transferWorker == null)
			{
				this._transferWorker = BackgroundProcessing.Create<TransferFilesArgs, ProgressEventArgs>(BackgroundMethods.TransferFiles,
					TransferBackgroundThread_CopyProgressChanged,
					TransferBackgroundThread_CopyCompleted,
					TransferBackgroundThread_OnException,
					args);

				this._transferWorker.Run();
			}
		}

		private void TransferBackgroundThread_CopyProgressChanged(ProgressEventArgs args)
		{
		}

		private void TransferBackgroundThread_CopyCompleted()
		{
			this._transferWorker.Dispose();
			this._transferWorker = null;

			this.View.TransferFinished(this.TransferCount);

			// is media player connected...
			// does anything need transferring ???
			this.View.EnableTransfer(false);
		}

		#endregion File Transfer Thread

		#region Clean up Thread

		private void SpinUpCleanupThread(CleanupFolderArgs args)
		{
			if (this._cleanupWorker == null)
			{
				this._cleanupWorker = BackgroundProcessing.Create<CleanupFolderArgs, ProgressEventArgs>(BackgroundMethods.CleanupFolder,
						CleanupBackgroundThread_ProgressChanged,
						CleanupBackgroundThread_RunWorkerCompleted,
						CleanupBackgroundThread_OnException,
						args);

				this._cleanupWorker.Run();
			}
		}
		
		private void CleanupBackgroundThread_ProgressChanged(ProgressEventArgs args)
		{
			/* nothing */
		}

		private void CleanupBackgroundThread_RunWorkerCompleted()
		{
			this._cleanupWorker.Dispose();
			this._cleanupWorker = null;
		}

		private void CleanupBackgroundThread_OnException(Exception ex)
		{
			this._cleanupWorker.Dispose();
			this._cleanupWorker = null;

			if (ex.InnerException != null)
				this.View.ReportError(ex.InnerException.Message);
			else
				this.View.ReportError(ex.Message);
		}

		#endregion Clean up Thread

		#endregion Background Thread Event Handlers

	}
}
